
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

import javax.media.opengl.*;
import javax.media.opengl.glu.*;

import org.geom.Vector3D;
import org.geom.Vector3D;

import com.sun.opengl.util.*; 


public class TestRay
extends JFrame
implements GLEventListener, KeyListener, MouseListener, MouseMotionListener
{
	
	//---------------------------------------------------------------------- class vars

	private static final long serialVersionUID = 1L;

	// opengl stuffs
	private GLCapabilities caps;
	private GLJPanel canvas;
	private GLU glu = new GLU();

	// selection
	private Point pickPoint = new Point();
	private boolean detect = true;
	
	// point detection
	Vector3D x0 = new Vector3D(0, 0, 0);
	float d = 0;

	//---------------------------------------------------------------------- main, constructors and inits
	public static void main(String[] args)
	{
		System.out.println("main");
		new TestRay().run();
	}

	public TestRay()
	{
		super("Test ray near point");
		System.out.println(" . ");

		caps = new GLCapabilities();
		caps.setHardwareAccelerated(true);

		canvas = new GLJPanel();// GLCanvas(caps);
		canvas.addGLEventListener(this);
		canvas.addKeyListener(this);
		canvas.addMouseListener(this);
		canvas.addMouseMotionListener(this);

		getContentPane().add(canvas);
		setResizable(true);

		final Animator anim = new Animator(canvas);
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				anim.stop();
				System.exit(0);
			}
		});
		anim.start();
	}

	public void run()
	{
		System.out.println("run");
		setSize(400, 400);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setVisible(true);
		canvas.requestFocusInWindow();
	}

	public void init(GLAutoDrawable drawable)
	{
		System.out.println("init");
		GL gl = drawable.getGL();

		gl.glShadeModel(GL.GL_SMOOTH);              // Enable Smooth Shading
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);    // Black Background
		gl.glClearDepth(1.0f);                      // Depth Buffer Setup
		gl.glEnable(GL.GL_DEPTH_TEST);              // Enables Depth Testing
		gl.glDepthFunc(GL.GL_LEQUAL);               // The Type Of Depth Testing To Do
		gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST); // Really Nice Perspective Calculations
	}

	//---------------------------------------------------------------------- opengl rendering
	public void reshape(GLAutoDrawable gLDrawable, int x, int y, int width, 
			int height) {

		System.out.println("reshape");

		final GL gl = gLDrawable.getGL();

		if (height <= 0) // avoid a divide by zero error!
			height = 1;
		final float h = (float) width / (float) height;
		gl.glViewport(0, 0, width, height);
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(45.0f, h, 1.0, 200.0);
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
	}

	Vector3D t = new Vector3D();
	public void display(GLAutoDrawable drawable)
	{
		//System.out.println("display");
		GL gl = drawable.getGL();

		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		gl.glLoadIdentity();
		
		gl.glPushMatrix();
		gl.glTranslatef(-1.5f, 0.0f, -60.0f);
		
		gl.glTranslatef(t.x, t.y, t.z);
		
		draw(gl);
		if(detect)performSelection(gl);
		
		gl.glPopMatrix();
		gl.glFlush();
	}

	private void draw(GL gl)
	{		
		//System.out.println("drawRects");
		if(d<0.05)gl.glColor3f(1f,1f,1f);
		else if(d<0.1)gl.glColor3f(0f,1f,1f);
		else if(d<0.2)gl.glColor3f(0.9f,0.9f,0.3f);
		else if(d<0.3)gl.glColor3f(0.9f,0.3f,0.3f);	
		else if(d<0.5)gl.glColor3f(0.6f,0.3f,0.3f);
		else gl.glColor3f(0.3f,0.3f,0.3f);
		gl.glPointSize(20);
		gl.glBegin(GL.GL_POINT);
		gl.glVertex3d(x0.x, x0.y, x0.z);
		gl.glEnd();
	}

	//---------------------------------------------------------------------- picking happiness

	private void performSelection(GL gl) {
		
		int viewport[] = new int[4];
	    double model_view[] = new double[16];
	    double projection[] = new double[16];
	    double wcoord[] = new double[4];// wx, wy, wz;// returned xyz coords
	    
	    int x = (int) pickPoint.getX(), y = (int) pickPoint.getY();
	    int realy = 0;// GL y coord pos
		
		gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
        gl.glGetDoublev(GL.GL_MODELVIEW_MATRIX, model_view, 0);
        gl.glGetDoublev(GL.GL_PROJECTION_MATRIX, projection, 0);
        
        realy = viewport[3] - y;
        System.out.println(pickPoint);
        System.out.println(viewport[0]+" "+viewport[1]+" "+viewport[2]+" "+viewport[3]);
        System.out.println("Coordinates at cursor are (" + x + ", " + realy);
        
        glu.gluUnProject( x,  realy, 0, //
            model_view, 0, projection, 0, viewport, 0, wcoord, 0);
        System.out.println("World coords at z=0.0 are ( "+ wcoord[0] + ", " + wcoord[1] + ", " + wcoord[2]+ ")");
        Vector3D x1 = new Vector3D(wcoord[0], wcoord[1], wcoord[2]);  
        
        glu.gluUnProject( x,  realy, 1, //
            model_view, 0, projection, 0, viewport, 0, wcoord, 0);
        //System.out.println("World coords at z=1.0 are ("+ wcoord[0] + ", " + wcoord[1] + ", " + wcoord[2]+ ")");
        Vector3D x2 = new Vector3D(wcoord[0], wcoord[1], wcoord[2]);

        // collision detection between ray x1-x2 and point x0
        d = Vector3D.crossProduct(x0.minus(x1), x0.minus(x2)).mag() /
        	(x2.minus(x1)).mag();
                
        System.out.println(d);
	}

	//---------------------------------------------------------------------- listeners

	public void mousePressed(MouseEvent mouse)
	{
		pickPoint = mouse.getPoint();
		t.x = (float) Math.random();
		t.y = (float) Math.random();
		t.z = (float) Math.random()*4;
		
		x0.x = (float) Math.random()*4;
		x0.y = (float) Math.random()*4;
		x0.z = (float) Math.random()*4;
	}

	public void mouseDragged(MouseEvent e) {
		pickPoint = e.getPoint();
	}

	public void mouseMoved(MouseEvent e) {
		pickPoint = e.getPoint();
	}

	public void keyTyped(KeyEvent key){}
	public void keyPressed(KeyEvent key){}
	public void keyReleased(KeyEvent key) {}
	public void mouseClicked(MouseEvent mouse) {}
	public void mouseReleased(MouseEvent mouse)	{}
	public void mouseEntered(MouseEvent mouse) {}
	public void mouseExited(MouseEvent mouse) {}
	public void displayChanged(GLAutoDrawable arg0, boolean arg1, boolean arg2) {}

}
